from fastapi import Request, HTTPException, status
from fastapi.responses import JSONResponse
from utils.jwt_util import verify_token, should_refresh_token, encode_token
from models import COLLECTION_NAMES
from models.db import get_mongo_db, get_redis_client
from bson import ObjectId
from typing import Optional
import json

async def auth_middleware(request: Request, call_next):
    """认证中间件，支持Redis token验证和自动刷新"""
    # OPTIONS 请求不需要认证（CORS 预检请求）
    if request.method == "OPTIONS":
        return await call_next(request)
    
    # 获取数据库连接实例
    mongo_db = get_mongo_db()
    redis_client = get_redis_client()
    
    # 不需要认证的路径
    excluded_paths = [
        "/auth/login", "/auth/register", "/static/", "/api/auth/login", "/api/auth/register", "/api/auth/logout", "/api/auth/refresh-token"
    ]
    
    # 精确匹配的路径（不使用 startswith）
    exact_match_paths = ["/", "/favicon.ico"]
    
    # 检查路径是否需要认证
    path = request.url.path
    
    # 精确匹配检查
    if path in exact_match_paths:
        return await call_next(request)
    
    # 前缀匹配检查
    if any(path.startswith(excluded) for excluded in excluded_paths):
        return await call_next(request)
    
    # 获取Authorization头
    auth_header = request.headers.get("Authorization")
    if not auth_header:
        return JSONResponse(
            status_code=status.HTTP_401_UNAUTHORIZED,
            content={
                "code": status.HTTP_401_UNAUTHORIZED,
                "message": "未授权访问",
                "status": "error"
            }
        )
    
    # 提取token
    try:
        scheme, token = auth_header.split(" ")
        if scheme.lower() != "bearer":
            raise ValueError("Invalid authentication scheme")
    except ValueError:
        return JSONResponse(
            status_code=status.HTTP_401_UNAUTHORIZED,
            content={
                "code": status.HTTP_401_UNAUTHORIZED,
                "message": "无效的认证格式",
                "status": "error"
            }
        )
    
    # 验证token
    payload = verify_token(token)
    if not payload:
        return JSONResponse(
            status_code=status.HTTP_401_UNAUTHORIZED,
            content={
                "code": status.HTTP_401_UNAUTHORIZED,
                "message": "令牌已过期或无效",
                "status": "error"
            }
        )
    
    # 获取用户信息和token_id
    user_id = payload.get("sub")
    token_id = payload.get("token_id")
    if not user_id or not token_id:
        return JSONResponse(
            status_code=status.HTTP_401_UNAUTHORIZED,
            content={
                "code": status.HTTP_401_UNAUTHORIZED,
                "message": "令牌中缺少必要信息",
                "status": "error"
            }
        )
    
    # 在Redis中验证token是否存在
    redis_key = f"user:{user_id}:tokens"
    token_data_str = redis_client.hget(redis_key, token_id)
    if not token_data_str:
        return JSONResponse(
            status_code=status.HTTP_401_UNAUTHORIZED,
            content={
                "code": status.HTTP_401_UNAUTHORIZED,
                "message": "令牌已过期或已被撤销",
                "status": "error"
            }
        )
    
    # 从数据库获取用户信息
    user = mongo_db[COLLECTION_NAMES["users"]].find_one({"_id": ObjectId(user_id)})
    if not user:
        return JSONResponse(
            status_code=status.HTTP_401_UNAUTHORIZED,
            content={
                "code": status.HTTP_401_UNAUTHORIZED,
                "message": "用户不存在",
                "status": "error"
            }
        )
    
    # 将用户信息添加到请求状态中
    request.state.user = user
    request.state.token = token
    request.state.payload = payload
    
    # 继续处理请求
    response = await call_next(request)
    
    # 检查是否需要刷新token
    if should_refresh_token(payload):
        # 创建新token
        new_token = encode_token(
            data={
                "sub": user_id,
                "username": payload.get("username"),
                "email": payload.get("email")
            }
        )
        
        # 获取新token的token_id
        new_payload = verify_token(new_token)
        if new_payload:
            new_token_id = new_payload.get("token_id")
            # 存储新token到Redis
            new_token_data = {
                "token": new_token,
                "created_at": "now()",
                "expires_at": "now() + 86400"
            }
            redis_client.hset(redis_key, new_token_id, json.dumps(new_token_data))
            redis_client.expire(redis_key, 86400)  # 1天过期
            
            # 删除旧token
            redis_client.hdel(redis_key, token_id)
            
            # 将新token添加到响应头
            response.headers["X-Refresh-Token"] = new_token
    
    return response

async def admin_middleware(request: Request, call_next):
    """管理员权限中间件"""
    # 检查路径是否需要管理员权限
    path = request.url.path
    if path.startswith("/admin/"):
        # 确保用户已认证
        user = getattr(request.state, "user", None)
        if not user:
            return JSONResponse(
                status_code=status.HTTP_401_UNAUTHORIZED,
                content={
                    "code": status.HTTP_401_UNAUTHORIZED,
                    "message": "未授权访问",
                    "status": "error"
                }
            )
        
        # 检查是否为管理员
        if not user.get("is_admin", False):
            return JSONResponse(
                status_code=status.HTTP_403_FORBIDDEN,
                content={
                    "code": status.HTTP_403_FORBIDDEN,
                    "message": "权限不足",
                    "status": "error"
                }
            )
    
    # 继续处理请求
    response = await call_next(request)
    return response